﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace GraphBuilder
{
    public partial class GraphBox : UserControl
    {

        #region Свойства
        public bool FixVertexSize { get { return m_FixedVertexSize; } set { m_FixedVertexSize = value; } }
        public int VertexHeight { get { return m_VertexHeight; } set { m_VertexHeight = value; } }
        public int VertexWidth { get { return m_VertexWidth; } set { m_VertexWidth = value; } }
        #endregion

        #region События
        public delegate void ElementHandler(Element item, Graph gr);
        [Browsable(true)]
        public event ElementHandler BeforeAddElement;
        [Browsable(true)]
        public event ElementHandler AfterAddElement;

        [Browsable(true)]
        public event ElementHandler BeforePasteElement;
        [Browsable(true)]
        public event ElementHandler AfterPasteElement;

        [Browsable(true)]
        public event ElementHandler BeforeRemoveElement;
        [Browsable(true)]
        public event ElementHandler AfterRemoveElement;

        public delegate void ChangeSelectionHandler(Element item, Graph gr);
        [Browsable(true)]
        public event ChangeSelectionHandler BeforeChangeSelection;
        [Browsable(true)]
        public event ChangeSelectionHandler AfterChangeSelection;

        public delegate void PreviewKeyDownHandler(object sender, KeyEventArgs e);
        [Browsable(true)]
        public event PreviewKeyDownHandler BeforeKeyDown;
        [Browsable(true)]
        public event PreviewKeyDownHandler AfterKeyDown;


        #endregion События

        #region Переменные для свойств
        private bool m_FixedVertexSize = false;
        private int m_VertexHeight = 61;
        private int m_VertexWidth = 81;
        #endregion


        private Point tPoint;
        private Point tFromPoint;
        private Point tToPoint;
        private Port tFromPort = null;
        private Port tToPort = null;
        private Vertex tResizeVertex = null;
        private Point tContextMenuLocation = new Point(0, 0);
        private bool ToolTipShowing = false;
        //public bool DrawWithNames()
        private void CheckPointsOrder(ref Point FromPoint, ref Point ToPoint)
        {
            if (FromPoint.X > ToPoint.X)
            {
                int tX;
                tX = FromPoint.X;
                FromPoint.X = ToPoint.X;
                ToPoint.X = tX;
            }
            if (FromPoint.Y > ToPoint.Y)
            {
                int tY;
                tY = FromPoint.Y;
                FromPoint.Y = ToPoint.Y;
                ToPoint.Y = tY;
            }
        }

        public Graph graph = new Graph();

        public GraphBox()
        {
            InitializeComponent();

            pbGraph.Width = 5000;
            pbGraph.Height = 5000;
            graph.owner = pbGraph;
            graph.gb = this;
            //graph.canvas = Graphics.FromHwnd(pbGraph.Handle);
        }


        #region Реакция на события мыши
        private void pbGraph_MouseDown(object sender, MouseEventArgs e)
        {
            //pbGraph.Focus();
            
            Element sitem;
            if (graph.ContainPoint(e.Location, out sitem))
            {
                if ((sitem is Vertex) || (sitem is Edge))
                {
                    if (BeforeChangeSelection != null) BeforeChangeSelection(sitem, graph);
                    graph.SelItem = sitem;
                    if (AfterChangeSelection != null) AfterChangeSelection(sitem, graph);
                }
            }
            pbGraph.Refresh();

            if (e.Button == MouseButtons.Left)
            {

                switch (graph.DrawMode)
                {
                    case TDrawMode.Vertex:
                        {
                            tFromPoint = e.Location;
                            break;
                        }
                    case TDrawMode.Edge:
                        {
                            Element item;
                            tFromPort = null;
                            if (graph.ContainPoint(e.Location, out item))
                                if (item is Port)
                                    if ((item as Port).Type == TPortType.Out)
                                        tFromPort = item as Port;
                                    else tToPort = null;

                            break;
                        }
                    case TDrawMode.Edit:
                        {
                            tPoint = e.Location;

                            Element item;
                            if (graph.ContainPoint(tPoint, out item))
                                if (item is Vertex)
                                {
                                    tResizeVertex = item as Vertex;
                                }
                            break;
                        }
                    case TDrawMode.None:
                        {
                            break;
                        }
                    default:
                        {
                            break;
                        }
                }
            }
            else
            {
                // если курсор находится над выделенным элементом, то будет вызвано КМ элемента
                
                    if ((graph.SelItem != null)&&(graph.SelItem.ContainPoint(e.Location)))
                        graph.OnMouseDown(pbGraph, e);
                    else
                    {
                        // иначе, если курсор находится над пустым холстом
                        // и есть скопированный элемент, то будет вызвано КМ холста
                        // если в КМ будет несколько пунктов, то расположение if надо будет поменять
                        if (graph.CopiedItem != null)
                        {
                            tContextMenuLocation = e.Location; 
                            ContextMenu context_menu = new ContextMenu();
                            context_menu.MenuItems.Add(new MenuItem("Вставить", new EventHandler((this.PasteCopied))));
                            context_menu.Show(this, new Point(e.X, e.Y));
                        }
                    }

                pbGraph.Refresh();

                //pbGraph.Refresh();
//                graph.Draw(Graphics.FromHwnd(pbGraph.Handle));

            }
            
        }

        // вставка скопированной вершины 
        private void PasteCopied(object sender, EventArgs e)
        {
            Vertex vertex;

            vertex = new Vertex(new Rectangle(tContextMenuLocation.X, tContextMenuLocation.Y, graph.CopiedItem.Rect.Width, graph.CopiedItem.Rect.Height));
            
            if (BeforePasteElement != null) BeforePasteElement(vertex, graph);
            if (vertex.Ports.Count > 0)
            {
                graph.AddElement(vertex);
                if (BeforeChangeSelection != null) BeforeChangeSelection(vertex, graph);
                graph.SelItem = vertex;
                if (AfterChangeSelection != null) AfterChangeSelection(vertex, graph);
            }
            if (AfterPasteElement != null) AfterPasteElement(vertex, graph);

            pbGraph.Refresh();

            //graph.CopiedItem = null;
        }

        private void pbGraph_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                Element sitem;
                if (graph.ContainPoint(e.Location, out sitem))
                {
                    if ((sitem is Vertex) || (sitem is Edge))
                    {
                        if (BeforeChangeSelection != null) BeforeChangeSelection(sitem, graph);
                        graph.SelItem = sitem;
                        if (AfterChangeSelection != null) AfterChangeSelection(sitem, graph);
                    }
                }
                pbGraph.Refresh();

                switch (graph.DrawMode)
                {
                    case TDrawMode.Vertex:
                        {
                            tToPoint = e.Location;
                            CheckPointsOrder(ref tFromPoint,ref tToPoint);

                            Vertex vertex;

                            if (!FixVertexSize)
                                vertex = new Vertex(new Rectangle(tFromPoint.X, tFromPoint.Y, tToPoint.X - tFromPoint.X, tToPoint.Y - tFromPoint.Y));
                            else
                                vertex = new Vertex(new Rectangle(tFromPoint.X, tFromPoint.Y, VertexWidth, VertexHeight));

                            if (BeforeAddElement != null) BeforeAddElement(vertex, graph);
                            if (vertex.Ports.Count > 0)
                            {
                                graph.AddElement(vertex);
                                if (BeforeChangeSelection != null) BeforeChangeSelection(vertex, graph);
                                graph.SelItem = vertex;
                                if (AfterChangeSelection != null) AfterChangeSelection(vertex, graph);
                            }
                            if (AfterAddElement != null) AfterAddElement(vertex, graph);

                            pbGraph.Refresh();

                            break;
                        }
                    case TDrawMode.Edge:
                        {
                            Element item;
                            tToPort = null;
                            if (graph.ContainPoint(e.Location, out item))
                                if (item is Port)
                                    if ((item as Port).Type == TPortType.In)
                                        tToPort = item as Port;
                                    else tToPort = null;

                            if ((tFromPort != null)&&(tToPort != null))
                            {
                                string name_dest = tToPort.Owner.ClassName;
                                if ((name_dest != "ctem.samples.actors.sequential.Booster")&&
                                    (name_dest != "ctem.samples.actors.sequential.InertiaLink")&&
                                    (name_dest != "ctem.samples.actors.sequential.Aperiodic2")&&
                                     (name_dest != "ctem.samples.actors.sequential.Oscillator")&&
                                     (name_dest != "ctem.samples.actors.sequential.Integrator")&&
                                     (name_dest != "ctem.samples.actors.sequential.AperiodicIntegr")&&
                                     (name_dest != "ctem.samples.actors.sequential.IdealDiff")&&
                                     (name_dest != "ctem.samples.actors.sequential.Diff")&&
                                     (name_dest != "ctem.samples.actors.sequential.CommonW"))
                                {
                                    Edge edge = new Edge(tFromPort, tToPort);
                                    if (BeforeAddElement != null) BeforeAddElement(edge, graph);
                                    graph.AddElement(edge);
                                    if (AfterAddElement != null) AfterAddElement(edge, graph);
                                }
                                else
                                    if (graph.GetEdgesForPort(tToPort).Count == 0)
                                    {
                                        Edge edge = new Edge(tFromPort, tToPort);
                                        if (BeforeAddElement != null) BeforeAddElement(edge, graph);
                                        graph.AddElement(edge);
                                        if (AfterAddElement != null) AfterAddElement(edge, graph);
                                    }
                            }
                            pbGraph.Refresh();
                            break;
                        }
                    case TDrawMode.Edit:
                        {
                            break;
                        }
                    case TDrawMode.None:
                        {
                            break;
                        }
                    default:
                        {
                            break;
                        }
                }
            }
        }


        private void pbGraph_MouseMove(object sender, MouseEventArgs e)
        {

            switch (e.Button)
            {
                case MouseButtons.Left:
                    {
                        switch (graph.DrawMode)
                        {
                            case TDrawMode.Vertex:
                                {
                                    if (!FixVertexSize)
                                    {
                                        Point tP1 = e.Location;
                                        Point tP0 = tFromPoint;
                                        CheckPointsOrder(ref tP0, ref tP1);
                                        pbGraph.Refresh();
                                        Vertex v = new Vertex(new Rectangle(tP0.X, tP0.Y, tP1.X - tP0.X, tP1.Y - tP0.Y));

                                        if (BeforeChangeSelection != null) BeforeChangeSelection(v, graph);
                                        graph.SelItem = v;
                                        if (AfterChangeSelection != null) AfterChangeSelection(v, graph);

                                        v.Draw(Graphics.FromHwnd(pbGraph.Handle), Color.Black);
                                    }
                                    break;
                                }
                            case TDrawMode.Edge:
                                {
                                    pbGraph.Refresh();
                                    //Point p = e.Location;
                                    Vertex tToV = new Vertex(new Rectangle(e.Location.X, e.Location.Y, 2, 2));
                                    Port tToP = new Port(tToV, TPortType.In);
                                    tToV.AddPort(tToP);
                                    Edge edge = new Edge(tFromPort, tToP);
                                    edge.Draw(Graphics.FromHwnd(pbGraph.Handle), Color.Black);
                                    break;
                                }
                            case TDrawMode.Edit:
                                {
                                    pbGraph.Refresh();
                                    if (graph.EditMode == TEditMode.Move)
                                    {
                                        Element item;
                                        if (graph.ContainPoint(e.Location, out item))
                                            if (item is Vertex)
                                            {
                                                (item as Vertex).MoveWithPorts(new Point((item as Vertex).Rect.Left + e.X - tPoint.X, (item as Vertex).Rect.Top + e.Y - tPoint.Y));
                                                if (BeforeChangeSelection != null) BeforeChangeSelection((item as Vertex), graph);
                                                graph.SelItem = (item as Vertex);
                                                if (AfterChangeSelection != null) AfterChangeSelection((item as Vertex), graph);
                                                tPoint = e.Location;
                                            }
                                    } else
                                        if (graph.EditMode == TEditMode.Resize)
                                            if (!FixVertexSize)
                                            {
                                                tResizeVertex.ResizeWithPorts(tPoint, e.Location);
                                                if (BeforeChangeSelection != null) BeforeChangeSelection(tResizeVertex, graph);
                                                graph.SelItem = tResizeVertex;
                                                if (AfterChangeSelection != null) AfterChangeSelection(tResizeVertex, graph);
                                                tPoint = e.Location;
                                            }

                                    break;
                                }
                            case TDrawMode.None:
                                {

                                    break;
                                }
                            default:
                                {
                                    break;
                                }
                        }

                        break;
                    }
                case MouseButtons.None:
                    {
                        pbGraph.Cursor = graph.DefineCursor(e.Location);
                        if (((pbGraph.Cursor == Cursors.SizeNESW) ||
                            (pbGraph.Cursor == Cursors.SizeNS) ||
                            (pbGraph.Cursor == Cursors.SizeNWSE) ||
                            (pbGraph.Cursor == Cursors.SizeWE)) &&
                            (FixVertexSize))
                            pbGraph.Cursor = Cursors.Arrow;
                        if (!graph.DrawVertexNames)
                        {
                            Element item1;
                            ToolTip tt = new ToolTip();
                            if (graph.ContainPoint(e.Location, out item1))
                            {
                                if (item1 is Vertex)
                                {
                                    if (!ToolTipShowing)
                                    {
                                        ToolTipShowing = true;
                                        tt.ToolTipTitle = (item1 as Vertex).VisibleName;
                                        //tt.SetToolTip(this, "sdd");
                                        tt.UseAnimation = false;
                                        tt.UseFading = false;
                                        // tt.IsBalloon = true;
                                        tt.Show(item1.Name, this, e.X, e.Y - 10, 1000);
                                        //tt.Show(item1.Name, this, (item1 as Vertex).Rect.Right, (item1 as Vertex).Rect.Top, 1000);
                                    }
                                }
                                else ToolTipShowing = false;
                            }
                            else ToolTipShowing = false;
                        }
                        break;
                    }
            }

        }
        #endregion Реакция на события мыши

        private void pbGraph_Paint(object sender, PaintEventArgs e)
        {
            graph.Draw(e.Graphics);
        }

        //private void DeleteToolStripMenuItem_Click(object sender, EventArgs e)
        //{
        //    if (BeforeRemoveElement != null) BeforeRemoveElement(graph.SelItem, graph);
        //    if (graph.SelItem != null) graph.RemoveElement(graph.SelItem);
        //    if (AfterRemoveElement != null) AfterRemoveElement(graph.SelItem, graph);
        //    //перемещаем выделение на следующий элемент графа
        //    List<Vertex> vertexes = graph.GetAllVertex();
        //    if (vertexes.Count > 0)
        //        graph.SelItem = vertexes[0];
        //    else
        //        graph.SelItem = null;

        //    pbGraph.Refresh();
        //}

        private void pbGraph_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control)
            {
                if (e.KeyCode == Keys.Delete)
                {
                    //DeleteToolStripMenuItem_Click(sender, new EventArgs());
                    graph.RemoveSelected(sender, e);
                    //MessageBox.Show("eee"); ;
                }
            }
        }

        private void pbGraph_LoadCompleted(object sender, AsyncCompletedEventArgs e)
        {

        }

        //private void GraphBox_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        //{
        //    if (BeforeKeyDown != null) BeforeKeyDown(sender, e);
        //    pbGraph_PreviewKeyDown(sender, e);
        //    if (AfterKeyDown != null) AfterKeyDown(sender, e);

        //}

        public void KeyDn(object sender, KeyEventArgs e)
        {
            GraphBox_KeyDown(sender, e);
        }

        private void GraphBox_KeyDown(object sender, KeyEventArgs e)
        {
            if (BeforeKeyDown != null) BeforeKeyDown(sender, e);
            pbGraph_KeyDown(sender, e);
            if (AfterKeyDown != null) AfterKeyDown(sender, e);

        }


    }
}